﻿using System;
using System.Linq;
using System.Text;
using AppsGenerator.Generators;
using AppsGenerator.Utils;
using CommonLogging;
using AppsGenerator.Models;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using DBBuilder.DBDataStructure;

namespace AppsGenerator.WebGenerators.WebRepositoryGenerator
{
    /// <summary>
    /// The base class for generating partials of repository
    /// </summary>
    public abstract class PartialRepositoryGenerator : BaseGenerator
    {
        #region Fields

        /// <summary>
        /// The logger instance
        /// </summary>
        public readonly ILogger Logger = LoggerManager.GetLogger(typeof(RepositoryGenerator).Name);

        /// <summary>
        /// The instance of ClassComponents, this object will be updated when deserialization template file
        /// </summary>
        public ClassComponents ClassComponents { get; set; }

        #endregion

        #region Methods

        /// <summary>
        /// Deserialize template file
        /// </summary>
        public virtual void DeserializeTemplate()
        {
            var templateFile = Path.Combine(this.GetAppPath(), "Templates", "WebTemplates", this.XMLTemplate);

            using (XmlTextReader xreader = new XmlTextReader(templateFile))
            {
                xreader.Namespaces = false;
                try
                {
                    var x = new XmlSerializer(typeof(ClassComponents));
                    this.ClassComponents = x.Deserialize(xreader) as ClassComponents;
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Format("Cannot deserialize RepositoryTemplate, RepositoryTemplate => {0}", ex.Message));
                }
            }
        }

        /// <summary>
        /// Method to generate repository components
        /// </summary>
        /// <param name="tbl">The data source is corresponding with class which will be generated</param>
        public void GenerateContents(Table tbl)
        {
            this.Logger.InfoFormat("Generate Repository[{0}] --> Start", tbl.Name);
            Console.WriteLine("Generate Repository[{0}] --> Start", tbl.Name);

            try
            {
                var contents = new StringBuilder(BaseConsts.MAX_BYTES);

                // Generating Using region
                contents.AppendLine(ClassComponents.UsingPart.Replace(BaseConsts.ConstGlobalNameSpace, this.GlobalNameSpace));
                contents.AppendLine(string.Empty);

                // Generating Class region
                contents.AppendLine(string.Format("namespace {0}.Repositories", this.GlobalNameSpace));
                contents.Append("{");
                contents.AppendLine(ClassComponents.ClassPart.Comment.Replace(BaseConsts.ConstTable, tbl.Name));
                contents.Append(BaseConsts.TAB1).AppendLine(string.Format("public class {0}Repository", tbl.Name));
                contents.Append(BaseConsts.TAB1).AppendLine("{");

                // Generating Regions
                contents.Append(this.GenerateRegions(tbl));

                contents.Append(BaseConsts.TAB1).AppendLine("}");
                contents.Append("}");
                contents.AppendLine();

                // Save controller contents to a file
                this.SaveFile(contents.ToString().TrimStart(), string.Format("{0}Repository.cs", tbl.Name));
            }
            catch (Exception ex)
            {
                this.Logger.ErrorFormat("GenerateContents Error: {0}", ex.ToString());
                throw new Exception(ex.ToString());
            }

            this.Logger.InfoFormat("Generate Repository[{0}] <-- End", tbl.Name);
            Console.WriteLine("Generate Repository[{0}] <-- End", tbl.Name);
        }

        /// <summary>
        /// The method for generating regions
        /// </summary>
        /// <param name="tbl">Data source for generating</param>
        /// <returns></returns>
        private string GenerateRegions(Table tbl)
        {
            var contents = new StringBuilder(BaseConsts.MAX_BYTES);
            var primaryKey = tbl.Attributes.FirstOrDefault(n => n.IsPrimaryKey);
            var foreignKeys = tbl.Attributes.Where(n => n.IsForeignKey);

            // Generating Regions
            foreach (Region region in ClassComponents.ClassPart.Regions)
            {
                contents.Append(BaseConsts.TAB2).AppendLine(string.Format("#region {0}", region.RegionName));
                contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);

                foreach (Item item in region.Items)
                {
                    if (item.IsFKPattern == null)
                    {
                        contents.AppendLine(item.Content.Replace(BaseConsts.ConstTable, tbl.Name)
                                                .Replace(BaseConsts.ConstPluralizeTable, tbl.Name.Pluralize())
                                                .Replace(BaseConsts.ConstPrimaryKey, primaryKey.Name)
                                                .Replace(BaseConsts.ConstArgs, this.BuildArgs(primaryKey)));
                        contents.AppendLine(string.Empty);
                    }
                    else
                    {
                        var boolVal = false;

                        if (bool.TryParse(item.IsFKPattern, out boolVal))
                        {
                            if (!boolVal)
                            {
                                contents.AppendLine(item.Content.Replace(BaseConsts.ConstTable, tbl.Name)
                                                        .Replace(BaseConsts.ConstPluralizeTable, tbl.Name.Pluralize())
                                                        .Replace(BaseConsts.ConstPrimaryKey, primaryKey.Name)
                                                        .Replace(BaseConsts.ConstArgs, this.BuildArgs(primaryKey)));
                                contents.AppendLine(string.Empty);
                            }
                            else
                            {
                                // It's foreign key pattern
                                foreach (DBBuilder.DBDataStructure.Attribute fk in foreignKeys)
                                {
                                    // Skips a refer table if it is existent in Skip array
                                    if (!this.IsItemExistent(BaseConsts.SkipTables, fk.ReferTo))
                                    {
                                        contents.AppendLine(item.Content.Replace(BaseConsts.ConstTable, tbl.Name)
                                                                .Replace(BaseConsts.ConstPluralizeTable,
                                                                         tbl.Name.Pluralize())
                                                                .Replace(BaseConsts.ConstPrimaryKey, primaryKey.Name)
                                                                .Replace(BaseConsts.ConstForeignTable, fk.ReferTo)
                                                                .Replace(BaseConsts.ConstArgs, this.BuildArgs(fk))
                                                                .Replace(BaseConsts.ConstForeignKey, fk.Name));
                                        contents.AppendLine(string.Empty);
                                    }
                                }
                            }
                        }
                        else
                        {
                            throw new Exception(string.Format("Cannot parse IsFKPattern of item {0} in region {1}",
                                                              item.Content, region.RegionName));
                        }
                    }
                }

                contents.Append(BaseConsts.TAB2).AppendLine("#endregion");
                contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);
            }
            return contents.ToString();
        }

        #endregion
    }
}
